package com.jiezuo.flowable.listener;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.jiezuo.common.enums.FlowableEnums;
import com.jiezuo.common.enums.MessageEnum;
import com.jiezuo.common.util.StringUtil;
import com.jiezuo.common.utils.SpringUtil;
import com.jiezuo.flowable.mapper.MyMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
import org.flowable.common.engine.impl.event.FlowableEntityEventImpl;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 *@Description 全局监听-工作流待办消息提醒
 *             isFailOnException() 返回true => 此监听器中的发生异常，会终止工作流，同时将异常向上抛出，以此触发全局事务起作用！！！
 *@Author 庞留杰
 *@Date2021/5/8 14:46
 *@Version V1.0
 **/
@Slf4j
@Component
@RequiredArgsConstructor
public class GlobalTaskCreateListener implements FlowableEventListener {

    private final TaskService taskService;
    private final RuntimeService runtimeService;
    private final HistoryService historyService;

    @Override
    public void onEvent(FlowableEvent flowableEvent) {
        log.info(">>>>>>>>>>>>>>>>>> 进入工作流 TaskCreate 全局监听器 >>>>>>>>>>>>>>>>>>");
        MyMapper myMapper = SpringUtil.getObject(MyMapper.class);
//        WSClient wsClient = SpringUtil.getObject(WSClient.class);
        TaskEntity taskEntity = (TaskEntity) ((FlowableEntityEventImpl) flowableEvent).getEntity();
        String taskId = taskEntity.getId();

        /*************** 对于第一个节点，通常都是由自己发起的，所以不用再发消息了(start)*******************/
        String processInstanceId = taskEntity.getProcessInstanceId();
        List<String> activityTypeFilter = Lists.newArrayList("userTask");
        List<HistoricActivityInstance> collect = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId)
                .orderByHistoricActivityInstanceStartTime().asc().list()
                .stream().filter(his -> activityTypeFilter.contains(his.getActivityType())).collect(Collectors.toList());
        if(collect.size() == 0){
            return;
        }
        /*************** 对于第一个节点，通常都是由自己发起的，所以不用再发消息了(end)*******************/
        List<IdentityLink> idList = taskService.getIdentityLinksForTask(taskId);
        if (CollectionUtils.isEmpty(idList)) {
            return;
        }
        Set<String> userSet = Sets.newHashSet();// 防止重复
        // 获取接收人，此处从Identity获取，实际情况会更复杂
        idList.forEach(identityLink -> {
            if (StringUtil.isNotEmpty(identityLink.getUserId())) {// 用户
                userSet.add(identityLink.getUserId());
            }
            if (StringUtil.isNotEmpty(identityLink.getGroupId())) {// 用户组
//                List<String> user_ids = myMapper.findUser_ids(identityLink.getGroupId());
                List<String> user_ids = myMapper.findUserIdsByButtonId(identityLink.getGroupId());
                userSet.addAll(user_ids);
            }
        });
        if (CollectionUtils.isNotEmpty(userSet)) {
            List<Map<String, Object>> msgList = Lists.newArrayList();
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(taskEntity.getProcessInstanceId()).singleResult();
            // String key = processInstance.getProcessDefinitionKey();//例如：biddingBond,不知道为啥，flowable没有给他值
            String processDefinitionId = processInstance.getProcessDefinitionId();// bidKey:5:3957608d-a728-11eb-856b-5254001052dd
            String key = processDefinitionId.split(":")[0];
            String task_key = taskEntity.getTaskDefinitionKey();
            for (String user_id : userSet) {
                Map<String, Object> messageInfo = Maps.newHashMap();
                messageInfo.put("title", processInstance.getName());
                messageInfo.put("type_group", MessageEnum.MessageGroupEnum.我的工作.getType());
                messageInfo.put("type", MessageEnum.我的待办.getType());
                messageInfo.put("operator", processInstance.getStartUserId());//发布人
                messageInfo.put("content", taskEntity.getName());//发布内容
                messageInfo.put("user_id", user_id);//接收人
                messageInfo.put("business_id", processInstance.getBusinessKey());//业务ID
                messageInfo.put("is_check", "0");//是否查看，0为未查看，1为已查看
                FlowableEnums flowableEnums = FlowableEnums.getFlowableEnums(key, task_key);
                if (flowableEnums != null) {
                    messageInfo.put("router", flowableEnums.getRouter());//跳转路径 url
                }
                msgList.add(messageInfo);
            }
            //开始发送消息
//            Result saveResult = wsClient.pushMessageInfoBatchSave(msgList);
//            if (!saveResult.isSuccess()) {
//                log.error("保存消息失败：{}", msgList);
//                throw new BusinessRuntimeException(saveResult.getMessage());//调用失败，抛出异常
//            }
//            List<Map<String, Object>> maps = JsonUtils.jsonToListMap(saveResult.getResult() + "");
//            Result sentResult = wsClient.batchPush(maps);
        }
    }

    /**
     * 当侦听器执行抛出异常时，当前操作是否应该失败。
     * isFailOnException()方法确定：当引发事件、onEvent(…)方法抛出异常时的行为。
     * 当返回false时，将忽略异常。当返回true时，异常不会被忽略，并且会出现向上冒泡传递（bubble up），实际上会使当前正在执行的命令失败。
     * 如果事件是API调用（或任何其他事务性操作，例如作业执行）的一部分，则事务将回滚。
     * 如果事件侦听器中的行为不是业务关键型的（business-critical），那么建议返回false。
     */
    @Override
    public boolean isFailOnException() {
        return false;
        // return true; TODO 暂时不影响系统，后期可以改为true
    }

    /** 返回此事件监听器是否在事件发生时立即触发，还是在事务生命周期事件上触发(在提交或回滚之前/之后)。
     *
     * isFireOnTransactionLifecycleEvent()方法确定此事件侦听器是在事件发生时立即触发，还是在由getOnTransaction()方法确定的事务生命周期事件（transaction lifecycle event）上触发。
     * 事务生命周期事件（transaction lifecycle event）支持的值为：
     *
     * 已提交（COMMITTED）
     * 已回滚（ROLLED_BACK）
     * 提交中（COMMITTING）
     * 回滚中（ROLLINGBACK）
     */
    @Override
    public boolean isFireOnTransactionLifecycleEvent() {
        return false;
    }

    //如果非空，表示在当前事务的生命周期中触发事件的时间点。
    @Override
    public String getOnTransaction() {
        //事务提交后触发
        //return TransactionState.COMMITTED.name();
        return null;
    }
}